﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace Kocic.RepositoryManagement
{
    /// <summary>
    /// Base class for instances which allow managers to re-use a single instance of a data source.
    /// </summary>
    /// <typeparam name="TDataSource">The type of the data source to use within the execution context of this instance.</typeparam>
    public abstract class DataSourceExecutionContext<TDataSource> : IDisposable
        where TDataSource : class, new()
    {
        private static DataSourceExecutionContext<TDataSource> _executionContext;
        private TDataSource _dataSource;
        private Boolean _disposed = false;
        private string _dataString = string.Empty;

        /// <summary>
        /// Creates an instance of the DataSourceExecutionContext class.
        /// </summary>
        protected DataSourceExecutionContext()
        {
            this.InitializeDataSource();

            _executionContext = this;
        }

        /// <summary>
        /// Creates an instance of the DataSourceExecutionContext class with the provided connection string.
        /// <param name="connectionString">The connection string used to initialize the data context.</param>
        /// </summary>
        /// <exception cref="ArgumentNullException"></exception>
        protected DataSourceExecutionContext(string connectionString)
        {
            if (string.IsNullOrEmpty(connectionString))
            {
                throw new ArgumentNullException("connectionString", "Connection string cannot be null or empty");
            }

            _dataString = connectionString;

            this.InitializeDataSource();

            _executionContext = this;
        }

        /// <summary>
        /// Creates an instance of the DataSourceExecutionContext class with the provided data source instance.
        /// </summary>
        /// <param name="dataSource"></param>
        /// <exception cref="ArgumentNullException"></exception>
        protected DataSourceExecutionContext(TDataSource dataSource)
        {
            if (dataSource == null)
            {
                throw new ArgumentNullException("dataSource", "Data source cannot be null.");
            }

            _dataSource = dataSource;

            _executionContext = this;
        }

        /// <summary>
        /// Initializes the data source to an instance of type TDataSource.
        /// </summary>
        protected virtual void InitializeDataSource()
        {
            this._dataSource = new TDataSource();
        }

        #region IDisposable Members

        /// <summary>
        /// Disposes resources used by the instance of the context scope.
        /// </summary>
        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }

        #endregion

        /// <summary>
        /// Disposes unmanaged resources.
        /// </summary>
        /// <param name="disposing">Value indicating if to invoke the method from the IDisposable.Dispose implementation or from the finalizer.</param>
        protected virtual void Dispose(Boolean disposing)
        {
            if (disposing)
            {
                this._dataSource = null;
                this._disposed = true;
            }
        }

        /// <summary>
        /// Gets or sets the static instance of the DataSourceExecutionContext instance.
        /// </summary>
        protected static DataSourceExecutionContext<TDataSource> ExecutionContext
        {
            get { return DataSourceExecutionContext<TDataSource>._executionContext; }
            set { DataSourceExecutionContext<TDataSource>._executionContext = value; }
        }

        /// <summary>
        /// Gets or sets the connection string.
        /// </summary>
        public string DataString
        {
            get { return _dataString; }
            set { _dataString = value; }
        }

        /// <summary>
        /// Gets a reference to the object context that is created for the current usage.
        /// </summary>
        public static TDataSource CurrentDataSource
        {
            get
            {
                if (_executionContext != null)
                {
                    return _executionContext._dataSource;
                }
                else
                {
                    return null;
                }
            }
        }

        /// <summary>
        /// Gets or sets the data source.
        /// </summary>
        public TDataSource DataSource
        {
            get { return this._dataSource; }
            set { this._dataSource = value; }
        }

        /// <summary>
        /// Gets or sets the value indicating if the object is disposed or not.
        /// </summary>
        protected Boolean Disposed
        {
            get { return this._disposed; }
            set { this._disposed = value; }
        }
    }
}